package compilador.sintatico;

import compilador.geradorcodigo.GeradorCodigo;
import compilador.lexico.AnalisadorLexico;
import compilador.lexico.LexicoException;
import compilador.lexico.TipoToken;
import compilador.lexico.Token;
import compilador.semantico.AnalisadorSemantico;
import compilador.semantico.CategoriaSimbolo;
import compilador.semantico.PropriedadesFuncao;
import compilador.semantico.PropriedadesProcedimento;
import compilador.semantico.PropriedadesRegistro;
import compilador.semantico.PropriedadesSimboloBasico;
import compilador.semantico.PropriedadesTipo;
import compilador.semantico.PropriedadesVariavelOuConstante;
import compilador.semantico.PropriedadesVetor;
import compilador.semantico.TipoBasico;
import compilador.semantico.TipoErroSemantico;
import java.util.ArrayList;
import java.util.List;

/**
 * Faz o reconhecimento da gramática da linguagem, usando o analisador léxico
 * para obter e validar os tokens e o analisador semântico para validar as
 * regras semânticas.
 *
 * @author Carlos H V Pinto
 * @author Lucas S Bueno
 * @author Paulo G L Freire
 */
public final class AnalisadorSintatico {

    private AnalisadorLexico lexico;
    private AnalisadorSemantico semantico;
    private GeradorCodigo geradorC;
    private Token tokenAtual;

    /**
     * Cria e inicializa um analisador sintático.
     *
     * @param lexico O analisador léxico para fazer o reconhecimento dos tokens.
     * @param semantico O analisador semântico para fazer o reconhecimento das
     * declarações, compatibilidade de tipos e outras regras semânticas.
     */
    public AnalisadorSintatico(AnalisadorLexico lexico, AnalisadorSemantico semantico, GeradorCodigo geradorC) {
        this.lexico = lexico;
        this.semantico = semantico;
        this.geradorC = geradorC;
        this.tokenAtual = null;
    }

    /**
     * Inicia o processo de análise sintática recursiva preditiva à esquerda.
     * Caso ocorram erros, imprime a mensagem de erro e finaliza a compilação.
     */
    public void iniciaAnalise() {
        try {
            // Insere o escopo global
            semantico.insereEscopo();

            // Insere os tipos básicos na tabela de símbolos
            PropriedadesTipo pt1 = new PropriedadesTipo(TipoBasico.INTEIRO);
            PropriedadesTipo pt2 = new PropriedadesTipo(TipoBasico.LITERAL);
            PropriedadesTipo pt3 = new PropriedadesTipo(TipoBasico.LOGICO);
            PropriedadesTipo pt4 = new PropriedadesTipo(TipoBasico.REAL);
            pt1.setNome(TipoBasico.INTEIRO);
            pt1.setTipoBasico(TipoBasico.INTEIRO);
            pt2.setNome(TipoBasico.LITERAL);
            pt2.setTipoBasico(TipoBasico.LITERAL);
            pt3.setNome(TipoBasico.LOGICO);
            pt3.setTipoBasico(TipoBasico.LOGICO);
            pt4.setNome(TipoBasico.REAL);
            pt4.setTipoBasico(TipoBasico.REAL);
            semantico.insereSimbolo(TipoBasico.INTEIRO, pt1);
            semantico.insereSimbolo(TipoBasico.LITERAL, pt2);
            semantico.insereSimbolo(TipoBasico.LOGICO, pt3);
            semantico.insereSimbolo(TipoBasico.REAL, pt4);

            // Obtém o primeiro símbolo
            obterProximo();
            // Chama a regra raiz, que deriva todas as outras
            programa();

            //encerra o escopo do programa, removendo a útima tabela e consequentemente todos os seus elementos
            semantico.removeEscopo();
        } catch (SintaticoException erroSintatico) {
            System.err.println(erroSintatico.getMessage());
        } catch (LexicoException erroLexico) {
            System.err.println(erroLexico.getMessage());
        }

        //verifica se ocorreram erros semânticos para decidir se imprime código C ou não
        if (semantico.getQtdeErros() > 0) {
            System.out.println("Fim da compilacao");
        } else {
            // Escreve o código em C gerado
            System.out.println(geradorC.getCodigo());
        }
    }

    /**
     * Monta uma mensagem de erro sintático. Após a montagem da mensagem uma
     * exceção de erro sintático é lançada com esta mensagem.
     *
     * @throws SintaticoException
     */
    public void erro() {
        String mensagem = "Linha " + lexico.getLeitor().getLinha()
                + ": erro sintatico proximo a ";

        // Imprime o erro sintático
        if (tokenAtual == null) {
            mensagem += "EOF";
        } else {
            mensagem += tokenAtual.getLexema();
        }

        throw new SintaticoException(mensagem);
    }

    /**
     * Chama o analisador léxico para ler o próximo token do arquivo de entrada,
     * e atualiza o token atual.
     */
    public void obterProximo() {
        // Lê até encontrar um token, imprimindo possíveis erros léxicos
        do {
            tokenAtual = lexico.proximoToken();
        } while (!lexico.getLeitor().terminou() && tokenAtual == null);

        if (tokenAtual == null) {
            erro();
        }
    }

    /**
     * Tenta reconhecer um tipo de token, lançando erro em caso de falha
     *
     * @param tipo Tipo de token esperado
     * @return O token reconhecido, ou null caso o reconhecimento tenha falhado.
     */
    public Token reconheceToken(TipoToken tipo, boolean obrigatorio) {
        if (tipo == tokenAtual.getTipo()) {
            Token t = tokenAtual;
            obterProximo();
            return t;
        } else if (obrigatorio) {
            erro(); // Imprime o erro
        }

        return null;
    }

    /**
     * Tenta reconhecer um tipo de token, lançando erro em caso de falha
     *
     * @param tipos Vetor com os tipos de tokens esperados. Pelo menos um deles
     * precisa ser reconhecido.
     * @return O token reconhecido, ou null caso o reconhecimento tenha falhado.
     */
    public Token reconheceToken(TipoToken[] tipos, boolean obrigatorio) {
        // Tenta reconhecer um por um
        for (TipoToken tipo : tipos) {
            // Obtém o próximo símbolo e atualiza o token atual
            if (tipo == tokenAtual.getTipo() && !lexico.getLeitor().terminou()) {
                Token t = tokenAtual;
                obterProximo();
                return t;
            }
        }
        // Se chegou aqui, é porque não conseguiu reconhecer nenhum
        if (obrigatorio) {
            erro(); // Imprime o erro
        }
        return null;
    }

    /**
     * programa : declaracoes 'algoritmo' (declaracao_local)* (cmd)*
     * 'fimalgoritmo';
     */
    public void programa() {
        //gera códgio C para os includes necessários
        geradorC.insereCodigo("#include <stdio.h>\n#include <stdlib.h>\n\n");

        declaracoes();

        reconheceToken(TipoToken.PR_Algoritmo, true);
        geradorC.insereCodigo("int main() { \n"); //codigo C para declaração da função principal

        // (declaracao_local)*
        while (tokenAtual.getTipo() == TipoToken.PR_Declare
                || tokenAtual.getTipo() == TipoToken.PR_Constante
                || tokenAtual.getTipo() == TipoToken.PR_Tipo) {
            declaracaoLocal();
        }

        /// (cmd)*
        while (tokenAtual.getTipo() == TipoToken.PR_Leia
                || tokenAtual.getTipo() == TipoToken.PR_Escreva
                || tokenAtual.getTipo() == TipoToken.PR_Se
                || tokenAtual.getTipo() == TipoToken.PR_Caso
                || tokenAtual.getTipo() == TipoToken.PR_Para
                || tokenAtual.getTipo() == TipoToken.PR_Enquanto
                || tokenAtual.getTipo() == TipoToken.PR_Faca
                || tokenAtual.getTipo() == TipoToken.SR_Circunflexo
                || tokenAtual.getTipo() == TipoToken.Identificador
                || tokenAtual.getTipo() == TipoToken.PR_Retorne) {
            // Erro de comando 'retorne' não permitido neste escopo
            if (tokenAtual.getTipo() == TipoToken.PR_Retorne && !semantico.isFuncao()) {
                semantico.erro(TipoErroSemantico.ComandoRetorneNaoPermitido,
                        lexico.getLeitor().getLinha(), null);
            }
            cmd();
        }

        if (tokenAtual.getTipo() != TipoToken.PR_FimAlgoritmo) {
            erro();
        } else {
            geradorC.insereCodigo("return 0;\n"); //insere o retorno de sucesso da função main
        }

        //código C para o final do algoritmo (fechamento do escopo da função main)
        geradorC.insereCodigo("}");
    }

    /**
     * declaracoes : (declaracao_local | declaracao_global) declaracoes | ;
     */
    public void declaracoes() {
        boolean epsilon = false;

        switch (tokenAtual.getTipo()) {
            case PR_Declare:
            case PR_Constante:
            case PR_Tipo:
                declaracaoLocal();
                break;

            case PR_Procedimento:
            case PR_Funcao:
                declaracaoGlobal();
                break;

            default:
                epsilon = true;
        }
        if (!epsilon) {
            declaracoes();
        }
    }

    /**
     * declaracao_local : 'declare' variavel | 'constante' IDENTIFICADOR ':'
     * ('literal' | 'inteiro' | 'real' | 'logico') '=' (CADEIA_LITERAL |
     * NUMERO_INTEIRO | NUMERO_REAL | 'verdadeiro' | 'falso') | 'tipo'
     * IDENTIFICADOR ':' tipo ;
     */
    public void declaracaoLocal() {
        Token t = null;
        switch (tokenAtual.getTipo()) {
            case PR_Declare: {
                obterProximo();

                // Obtém as propriedades das variáveis                
                List<PropriedadesSimboloBasico> psbList = variavel();

                // insere todas as variáveis na tabela de símbolos
                for (PropriedadesSimboloBasico psb : psbList) {

                    //verifica se a variável é vetor ou não
                    if (psb instanceof PropriedadesVariavelOuConstante) {
                        PropriedadesVariavelOuConstante pvc = (PropriedadesVariavelOuConstante) psb;

                        //insere na tabela de símbolos verificando erro de identificador já declarado
                        if (!semantico.insereSimbolo(pvc.getLexema(), pvc)) {
                            semantico.erro(TipoErroSemantico.IdentificadorJaDeclarado, pvc.getLinha(), pvc.getLexema());
                        }
                    } else if (psb instanceof PropriedadesVetor) {
                        PropriedadesVetor pv = (PropriedadesVetor) psb;

                        //insere na tabela de símbolos verificando erro de identificador já declarado
                        if (!semantico.insereSimbolo(pv.getLexema(), pv)) {
                            semantico.erro(TipoErroSemantico.IdentificadorJaDeclarado, pv.getLinha(), pv.getLexema());
                        }
                    }
                }
                break;
            }

            case PR_Constante: {
                obterProximo();

                //insere o #define em código C da constante
                geradorC.insereCodigo("#define ");

                // Obtém o nome da constante
                String nomeConst = reconheceToken(TipoToken.Identificador, true).getLexema();
                geradorC.insereCodigo(nomeConst + " "); //insere o nome da constante no define

                PropriedadesVariavelOuConstante pc = new PropriedadesVariavelOuConstante(nomeConst, true);

                // Obtém o tipo (básico)
                reconheceToken(TipoToken.SR_DoisPontos, true);
                TipoToken[] tipos1 = {TipoToken.PR_Literal, TipoToken.PR_Inteiro,
                    TipoToken.PR_Real, TipoToken.PR_Logico};
                t = reconheceToken(tipos1, true);
                pc.setTipo(semantico.traduzTipoBasico(t.getTipo()));

                // Obtém o valor da constante
                reconheceToken(TipoToken.SR_Igual, true);
                TipoToken[] tipos2 = {TipoToken.CadeiaLiteral, TipoToken.NumeroInteiro,
                    TipoToken.NumeroReal, TipoToken.PR_Verdadeiro, TipoToken.PR_Falso};
                t = reconheceToken(tipos2, true);

                //se a constante for verdadeiro ou falso, então substitui para 1 ou 0
                //caso contrário, recupera a constante que deve ser atribuida para inserção do resto do código do #define
                String valorC;
                if (t.getTipo() == TipoToken.PR_Verdadeiro || t.getTipo() == TipoToken.PR_Falso) {
                    valorC = GeradorCodigo.converteOperador(t.getLexema());
                } else {
                    valorC = t.getLexema();
                }

                geradorC.insereCodigo(valorC + "\n\n"); //inserir o valor da constante

                if (!pc.getTipo().equals(semantico.traduzTipoBasico(t.getTipo()))) {
                    // Erro de atribuição não compatível
                    semantico.erro(TipoErroSemantico.AtribuicaoNaoCompativel,
                            lexico.getLeitor().getLinha(), nomeConst);
                } else {
                    pc.setValor(t.getLexema());

                    //tenta inserir o nome da constante na tabela de símbolos
                    if (!semantico.insereSimbolo(nomeConst, pc)) {
                        semantico.erro(TipoErroSemantico.IdentificadorJaDeclarado, lexico.getLeitor().getLinha(), nomeConst);
                    }
                }
                break;
            }

            case PR_Tipo: {
                obterProximo();

                //gera código C para redifinição de tipo
                geradorC.insereCodigo("typedef ");

                // Obtém o nome do novo tipo
                String nomeTipo = reconheceToken(TipoToken.Identificador, true).getLexema();
                reconheceToken(TipoToken.SR_DoisPontos, true);

                // Obtém as propriedades do tipo antigo
                PropriedadesTipo pt1 = tipo();

                //acrescenta ao código C o nome do novo tipo declarado seguido de ; e quebra de linha
                geradorC.insereCodigo(" " + nomeTipo + ";\n");

                if (pt1 != null) { // Será null se o tipo antigo não existir

                    if (pt1 instanceof PropriedadesRegistro) {
                        PropriedadesRegistro pr = (PropriedadesRegistro) pt1;
                        pr.setNome(nomeTipo);

                        //tenta inserir o nome do tipo na tabela de símbolos
                        if (!semantico.insereSimbolo(nomeTipo, pr)) {
                            semantico.erro(TipoErroSemantico.IdentificadorJaDeclarado, lexico.getLeitor().getLinha(), nomeTipo);
                        }
                    } else {
                        // Define as propriedades do novo tipo
                        PropriedadesTipo pt2 = new PropriedadesTipo(nomeTipo);
                        pt2.setNome(nomeTipo);
                        pt2.setTipoBasico(pt1.getTipoBasico());

                        //tenta inserir o nome do tipo na tabela de símbolos
                        if (!semantico.insereSimbolo(nomeTipo, pt2)) {
                            semantico.erro(TipoErroSemantico.IdentificadorJaDeclarado, lexico.getLeitor().getLinha(), nomeTipo);
                        }
                    }
                }
                break;
            }

            default:
                erro();
        }
    }

    /**
     * declaracao_global : 'procedimento' IDENTIFICADOR '(' (parametro)? ')'
     * (declaracao_local)* (cmd)* 'fimprocedimento' | 'funcao' IDENTIFICADOR '('
     * (parametro)? ')' ':' ('^')? (('literal' | 'inteiro' | 'real' | 'logico')
     * | IDENTIFICADOR) (declaracao_local)* (cmd)* 'fimfuncao' ;
     */
    public void declaracaoGlobal() {
        Token t = null;
        switch (tokenAtual.getTipo()) {
            case PR_Procedimento: {
                obterProximo();

                // Obtém o nome do procedimento
                String nomeProc = reconheceToken(TipoToken.Identificador, true).getLexema();
                PropriedadesProcedimento pp = new PropriedadesProcedimento(nomeProc);

                // Obtém as propriedades dos parâmetros
                reconheceToken(TipoToken.SR_AbreParenteses, true);

                //insere código C do procedimento
                geradorC.insereCodigo("void " + nomeProc + "(");

                //verifica se existem parâmetros
                if (tokenAtual.getTipo() != TipoToken.SR_FechaParenteses) {
                    //obtém lista de parâmetros
                    List<PropriedadesVariavelOuConstante> pvcList = parametro();

                    if (pvcList.size() > 0) { // Há parâmetros
                        List<String> tiposParams = new ArrayList<String>();
                        List<String> nomesParams = new ArrayList<String>();
                        for (int i = 0; i < pvcList.size(); i++) {
                            PropriedadesVariavelOuConstante prop = pvcList.get(i);

                            tiposParams.add(prop.getTipo());
                            nomesParams.add(prop.getLexema());

                            //insere no código C os parâmetros
                            if (i < (pvcList.size() - 1)) {
                                geradorC.insereCodigo(GeradorCodigo.converteTipo(prop.getTipo(), false) + prop.getValor() + " " + prop.getLexema() + ", ");
                            } else {
                                geradorC.insereCodigo(GeradorCodigo.converteTipo(prop.getTipo(), false) + prop.getValor() + " " + prop.getLexema());
                            }

                        }

                        //atribui os itens faltantes no objeto de PropriedadesProcedimento
                        pp.setQtdeParametros(pvcList.size());
                        pp.setTiposParametros(tiposParams);
                        pp.setNomesParametros(nomesParams);
                    } else { // Não há parâmetros
                        pp.setQtdeParametros(0);
                    }
                }

                reconheceToken(TipoToken.SR_FechaParenteses, true);

                geradorC.insereCodigo(") {\n"); //terminação da declaração do procedimento

                //tenta inserir na tabela de símbolos o nome do procedimento com suas propriedades
                if (!semantico.insereSimbolo(nomeProc, pp)) {
                    semantico.erro(TipoErroSemantico.IdentificadorJaDeclarado, lexico.getLeitor().getLinha(), pp.getLexema());
                }

                //inicia o escopo do procedimento
                semantico.insereEscopo();

                //insere na tabela de símbolos as variáveis contidas nos parâmetros do procedimento
                //é feito agora pois esses símbolos precisam ser inseridos em um escopo interno
                for (int i = 0; i < pp.getQtdeParametros(); i++) {
                    PropriedadesVariavelOuConstante pvc = new PropriedadesVariavelOuConstante(pp.getNomesParametros().get(i), false);
                    pvc.setTipo(pp.getTiposParametros().get(i));

                    //tenta inserir o nome do parâmetro na tabela de símbolos
                    if (!semantico.insereSimbolo(pvc.getLexema(), pvc)) {
                        semantico.erro(TipoErroSemantico.IdentificadorJaDeclarado, lexico.getLeitor().getLinha(), pvc.getLexema());
                    }

                    PropriedadesSimboloBasico psb = semantico.buscaSimbolo(pvc.getTipo());

                    //caso a variável seja um tipo registro, insere na tabela todos os seus membros
                    if (psb instanceof PropriedadesRegistro) {
                        PropriedadesRegistro pr = (PropriedadesRegistro) psb;

                        //percorre a lista de símbolos do registro para adicionar na tabela
                        for (PropriedadesSimboloBasico psbR : pr.getSimbolos()) {
                            String lex = pvc.getLexema() + "." + psbR.getLexema();

                            //verifica se o símbolo básico é variável ou vetor
                            if (psbR instanceof PropriedadesVariavelOuConstante) {
                                PropriedadesVariavelOuConstante pvc2 = (PropriedadesVariavelOuConstante) psbR;
                                PropriedadesVariavelOuConstante pvc3 = new PropriedadesVariavelOuConstante(lex, false);
                                pvc3.setTipo(pvc2.getTipo());

                                //tenta inserir o símbolo na tabela
                                if (!semantico.insereSimbolo(pvc3.getLexema(), pvc3)) {
                                    semantico.erro(TipoErroSemantico.IdentificadorJaDeclarado, lexico.getLeitor().getLinha(), pvc3.getLexema());
                                }

                            } else if (pr.getSimbolos().get(i) instanceof PropriedadesVetor) {
                                PropriedadesVetor pv = (PropriedadesVetor) psbR;

                                pv.setLexema(lex);

                                //tenta inserir o símbolo na tabela
                                if (!semantico.insereSimbolo(pv.getLexema(), pv)) {
                                    semantico.erro(TipoErroSemantico.IdentificadorJaDeclarado, lexico.getLeitor().getLinha(), pv.getLexema());
                                }
                            }
                        }
                    }
                }
                /// (declaracao_local)*
                while (tokenAtual.getTipo() == TipoToken.PR_Declare
                        || tokenAtual.getTipo() == TipoToken.PR_Constante
                        || tokenAtual.getTipo() == TipoToken.PR_Tipo) {
                    declaracaoLocal();
                }
                /// (cmd)*
                while (tokenAtual.getTipo() == TipoToken.PR_Leia
                        || tokenAtual.getTipo() == TipoToken.PR_Escreva
                        || tokenAtual.getTipo() == TipoToken.PR_Se
                        || tokenAtual.getTipo() == TipoToken.PR_Caso
                        || tokenAtual.getTipo() == TipoToken.PR_Para
                        || tokenAtual.getTipo() == TipoToken.PR_Enquanto
                        || tokenAtual.getTipo() == TipoToken.PR_Faca
                        || tokenAtual.getTipo() == TipoToken.SR_Circunflexo
                        || tokenAtual.getTipo() == TipoToken.Identificador
                        || tokenAtual.getTipo() == TipoToken.PR_Retorne) {

                    // Erro de comando 'retorne' não permitido neste escopo
                    if (tokenAtual.getTipo() == TipoToken.PR_Retorne && !semantico.isFuncao()) {
                        semantico.erro(TipoErroSemantico.ComandoRetorneNaoPermitido,
                                lexico.getLeitor().getLinha(), null);
                    }
                    cmd();
                }

                reconheceToken(TipoToken.PR_FimProcedimento, true);

                geradorC.insereCodigo("}\n"); //acaba com o escopo do procedimento

                //acaba com o escopo do procedimento
                semantico.removeEscopo();
                break;
            }

            case PR_Funcao: {
                //ativa o modo função, para não dar erros de retorno não permitido
                semantico.setFuncao(true);
                obterProximo();

                // Obtém o nome da função
                String nomeFunc = reconheceToken(TipoToken.Identificador, true).getLexema();
                PropriedadesFuncao pf = new PropriedadesFuncao(nomeFunc);

                // Obtém as propriedades dos parâmetros
                reconheceToken(TipoToken.SR_AbreParenteses, true);

                //variável que irá conter a declaração da função para geração do código C
                StringBuilder declFunc = new StringBuilder();
                declFunc.append(nomeFunc).append("(");

                if (tokenAtual.getTipo() != TipoToken.SR_FechaParenteses) {
                    // obtém as informações de parâmetros
                    List<PropriedadesVariavelOuConstante> pvcList = parametro();

                    if (pvcList.size() > 0) { // Há parâmetros
                        //recupera as informações de parâmetros para construir o objeto de PropriedadesFuncao
                        List<String> tiposSimbolos = new ArrayList<String>();
                        List<String> nomesSimbolos = new ArrayList<String>();

                        for (int i = 0; i < pvcList.size(); i++) {
                            PropriedadesVariavelOuConstante prop = pvcList.get(i);

                            tiposSimbolos.add(prop.getTipo());
                            nomesSimbolos.add(prop.getLexema());

                            //constroi a expressão de parâmetros para o código em C
                            if (i < (pvcList.size() - 1)) {
                                declFunc.append(GeradorCodigo.converteTipo(prop.getTipo(), false)).append(prop.getValor()).append(" ").append(prop.getLexema()).append(", ");
                            } else {
                                declFunc.append(GeradorCodigo.converteTipo(prop.getTipo(), false)).append(prop.getValor()).append(" ").append(prop.getLexema());
                            }
                        }

                        //atribui os itens faltantes para o objeto PropriedadesFuncao
                        pf.setQtdeParametros(pvcList.size());
                        pf.setTiposParametros(tiposSimbolos);
                        pf.setNomesParametros(nomesSimbolos);
                    } else { // Não há parâmetros
                        pf.setQtdeParametros(0);
                    }
                }

                reconheceToken(TipoToken.SR_FechaParenteses, true);
                reconheceToken(TipoToken.SR_DoisPontos, true);
                // Obtém o tipo de retorno da função
                // ('^')?

                Token pont = reconheceToken(TipoToken.SR_Circunflexo, false);
                StringBuilder tipoR = new StringBuilder(); //variável que guarda o tipo de retorno para a geração de código C

                // (('literal' | 'inteiro' | 'real' | 'logico') | IDENTIFICADOR)
                if (tokenAtual.getTipo() == TipoToken.Identificador) {
                    //controi o tipo de retorno para inserir no código C em seguida
                    tipoR.append(tokenAtual.getLexema()); //recupera o identificador 

                    //se o retorno for ponteiro, adiciona * ao tipo de retorno
                    if (pont != null) {
                        tipoR.append("*");
                    }

                    // Registro ou tipo definido pelo usuário
                    PropriedadesSimboloBasico psb = semantico.buscaSimbolo(tokenAtual.getLexema());

                    //verifica se o identificador é um tipo válido
                    if (psb != null
                            && (psb.getCategoria() == CategoriaSimbolo.Tipo
                            || psb.getCategoria() == CategoriaSimbolo.Registro)) {

                        PropriedadesTipo pt = (PropriedadesTipo) psb;
                        pf.setTipoRetorno(pt.getTipoBasico());
                    } else {
                        // Erro de tipo não declarado
                        semantico.erro(TipoErroSemantico.TipoNaoDeclarado,
                                lexico.getLeitor().getLinha(), tokenAtual.getLexema());
                    }
                    obterProximo();
                } else {
                    // Tipo básico
                    TipoToken[] tipos = {TipoToken.PR_Literal, TipoToken.PR_Inteiro,
                        TipoToken.PR_Real, TipoToken.PR_Logico};
                    t = reconheceToken(tipos, true);

                    //recupera o tipo básico fazendo as conversões para tipos em linguagem C
                    tipoR.append(GeradorCodigo.converteTipo(t.getLexema(), false));

                    //adiciona o * caso o tipo de retorno seja um ponteiro
                    if (pont != null) {
                        tipoR.append("*");
                    }

                    //atribui o tipo de retorno ao objeto de PropriedadeFuncao
                    pf.setTipoRetorno(semantico.traduzTipoBasico(t.getTipo()));
                }

                //insere as propriedades da função na tabela de símbolos
                if (!semantico.insereSimbolo(nomeFunc, pf)) {
                    semantico.erro(TipoErroSemantico.IdentificadorJaDeclarado, lexico.getLeitor().getLinha(), nomeFunc);
                }

                //insere a declaração da função na linguagem C
                geradorC.insereCodigo(tipoR + " " + declFunc + ") {\n");

                //inicia o escopo da função
                semantico.insereEscopo();

                //insere na tabela de símbolos as variáveis dos parâmetros da função
                //é feito agora pois os símbolos tem que ser inseridos em um escopo interno da função
                for (int i = 0; i < pf.getQtdeParametros(); i++) {
                    PropriedadesVariavelOuConstante pvc = new PropriedadesVariavelOuConstante(pf.getNomesParametros().get(i), false);
                    pvc.setTipo(pf.getTiposParametros().get(i));

                    //tenta inserir o nome do parâmetro na tabela de símbolos
                    if (!semantico.insereSimbolo(pvc.getLexema(), pvc)) {
                        semantico.erro(TipoErroSemantico.IdentificadorJaDeclarado, lexico.getLeitor().getLinha(), pvc.getLexema());
                    }

                    PropriedadesSimboloBasico psb = semantico.buscaSimbolo(pvc.getTipo());

                    //se a variável for registro, insere todas os seus membros
                    if (psb instanceof PropriedadesRegistro) {
                        PropriedadesRegistro pr = (PropriedadesRegistro) psb;

                        //percorre a lista de símbolos do registro para inserir seus membros
                        for (PropriedadesSimboloBasico psbR : pr.getSimbolos()) {
                            String lex = pvc.getLexema() + "." + psbR.getLexema();

                            //verifica se o símbolo básico é variável simples ou vetor
                            if (psbR instanceof PropriedadesVariavelOuConstante) {
                                PropriedadesVariavelOuConstante pvc2 = (PropriedadesVariavelOuConstante) psbR;
                                PropriedadesVariavelOuConstante pvc3 = new PropriedadesVariavelOuConstante(lex, false);
                                pvc3.setTipo(pvc2.getTipo());

                                //tenta imserir elemento na tabela de símbolos
                                if (!semantico.insereSimbolo(pvc3.getLexema(), pvc3)) {
                                    semantico.erro(TipoErroSemantico.IdentificadorJaDeclarado, lexico.getLeitor().getLinha(), pvc3.getLexema());
                                }

                            } else if (pr.getSimbolos().get(i) instanceof PropriedadesVetor) {
                                PropriedadesVetor pv = (PropriedadesVetor) psbR;

                                pv.setLexema(lex);

                                //tenta imserir elemento na tabela de símbolos
                                if (!semantico.insereSimbolo(pv.getLexema(), pv)) {
                                    semantico.erro(TipoErroSemantico.IdentificadorJaDeclarado, lexico.getLeitor().getLinha(), pv.getLexema());
                                }
                            }
                        }
                    }
                }

                // (declaracao_local)*
                while (tokenAtual.getTipo() == TipoToken.PR_Declare
                        || tokenAtual.getTipo() == TipoToken.PR_Constante
                        || tokenAtual.getTipo() == TipoToken.PR_Tipo) {
                    declaracaoLocal();
                }

                // (cmd)*
                while (tokenAtual.getTipo() == TipoToken.PR_Leia
                        || tokenAtual.getTipo() == TipoToken.PR_Escreva
                        || tokenAtual.getTipo() == TipoToken.PR_Se
                        || tokenAtual.getTipo() == TipoToken.PR_Caso
                        || tokenAtual.getTipo() == TipoToken.PR_Para
                        || tokenAtual.getTipo() == TipoToken.PR_Enquanto
                        || tokenAtual.getTipo() == TipoToken.PR_Faca
                        || tokenAtual.getTipo() == TipoToken.SR_Circunflexo
                        || tokenAtual.getTipo() == TipoToken.Identificador
                        || tokenAtual.getTipo() == TipoToken.PR_Retorne) {
                    cmd();
                }

                reconheceToken(TipoToken.PR_FimFuncao, true);
                geradorC.insereCodigo("}\n"); //encerra o escopo da função

                //termina o escopo da função
                semantico.removeEscopo();
                //desativa o modo função
                semantico.setFuncao(false);
                break;
            }

            default:
                erro();
        }
    }

    /**
     * variavel : identificador (',' identificador)* ':' tipo ;
     */
    public List<PropriedadesSimboloBasico> variavel() {
        List<PropriedadesSimboloBasico> psbList1 = new ArrayList<PropriedadesSimboloBasico>(); //lista de variáveis
        List<PropriedadesSimboloBasico> psbList2 = new ArrayList<PropriedadesSimboloBasico>(); //lista de elementos de registros

        int linha = lexico.getLeitor().getLinha(); //guarda o valor da linha da variável  

        // Obtém as propriedades dos identificadores
        PropriedadesSimboloBasico psb = identificador();
        psb.setLinha(linha);
        psbList1.add(psb);

        // (',' identificador)*
        //obtém propriedaes de mais identificadores caso existam
        while (tokenAtual.getTipo() == TipoToken.SR_Virgula) {
            obterProximo();
            linha = lexico.getLeitor().getLinha();
            psb = identificador();
            psb.setLinha(linha);
            psbList1.add(psb);
        }

        reconheceToken(TipoToken.SR_DoisPontos, true);

        // Obtém as propriedades do tipo
        PropriedadesTipo pt = tipo();

        //atribui o tipo reconhecido aos elementos recuperados anteriormente
        for (int i = 0; i < psbList1.size(); i++) {
            PropriedadesSimboloBasico p = psbList1.get(i);

            // Adiciona os membros do registro à tabela de símbolos
            // Também adiciona o novo registro à tabela de símbolos
            if (pt != null && pt.getCategoria() == CategoriaSimbolo.Registro) {
                PropriedadesRegistro pr = (PropriedadesRegistro) pt;
                for (int j = 0; j < pr.getSimbolos().size(); j++) {
                    String lex = p.getLexema() + "." + pr.getSimbolos().get(j).getLexema();

                    //verifica se o símbolo encontrado é uma variável simples ou vetor
                    //recupera o tipo do elemento do registro e adiciona objeto PropriedadesSimbolosBasicos na psbList2
                    if (pr.getSimbolos().get(j) instanceof PropriedadesVariavelOuConstante) {
                        PropriedadesVariavelOuConstante pvcT = (PropriedadesVariavelOuConstante) pr.getSimbolos().get(j);
                        PropriedadesVariavelOuConstante pvc = new PropriedadesVariavelOuConstante(lex, false);
                        pvc.setTipo(pvcT.getTipo());
                        pvc.setLinha(pvcT.getLinha());
                        psbList2.add(pvc);
                    } else if (pr.getSimbolos().get(j) instanceof PropriedadesVetor) {
                        PropriedadesVetor pvT = (PropriedadesVetor) pr.getSimbolos().get(j);
                        PropriedadesVetor pv = new PropriedadesVetor(lex);
                        pv.setTipo(pvT.getTipo());
                        pv.setLinha(pvT.getLinha());
                        psbList2.add(pv);
                    }
                }
            }

            //adiciona o tipo na variável
            if (pt != null) {

                //verifica se a variável é vetor ou não
                if (p instanceof PropriedadesVariavelOuConstante) {
                    PropriedadesVariavelOuConstante pvc = (PropriedadesVariavelOuConstante) p;
                    pvc.setTipo(pt.getNome());

                    //insere no código C os nomes das variáveis
                    if (i == 0) {
                        geradorC.insereCodigo(" ");
                    }

                    //verifica se a variável é ponteiro para adicionar * ao código C
                    if (pt.isPonteiro()) {
                        geradorC.insereCodigo("*");
                    }

                    //insere o nome da variável no código C
                    geradorC.insereCodigo(pvc.getLexema());

                    //se for literal insere TAMANHO_LITERAL ao final da variável, para representar um vetor de char (string)
                    if (pt.getNome().equals("literal")) {
                        geradorC.insereCodigo(GeradorCodigo.TAMANHO_LITERAL);
                    }

                    //verifica se é a última variável para adicionar ';' ao código C
                    if (i < (psbList1.size() - 1)) { //se não for última variável, só adiciona ','
                        geradorC.insereCodigo(", ");
                    } else {
                        geradorC.insereCodigo(";\n");
                    }

                } else if (p instanceof PropriedadesVetor) {
                    PropriedadesVetor pv = (PropriedadesVetor) p;

                    pv.setTipo(pt.getNome());

                    //insere no código C os nomes das variáveis
                    if (i == 0) {
                        geradorC.insereCodigo(" ");
                    }

                    //verifica se a variável é ponteiro para adicionar * ao código C
                    if (pt.isPonteiro()) {
                        geradorC.insereCodigo("*");
                    }

                    //insere o nome da variável no código C 
                    geradorC.insereCodigo(pv.getLexema());

                    //insere as informações de vetor (suas dimensões) no código C
                    for (String dim : pv.getTamanhoDimensoes()) {
                        geradorC.insereCodigo("[");
                        geradorC.insereCodigo(dim);
                        geradorC.insereCodigo("]");
                    }

                    //se for literal insere TAMANHO_LITERAL ao final da variável, para representar um vetor de char (string)
                    if (pt.getNome().equals("literal")) {
                        geradorC.insereCodigo(GeradorCodigo.TAMANHO_LITERAL);
                    }

                    //verifica se é a última variável para adicionar ; ao código C
                    if (i < (psbList1.size() - 1)) {
                        geradorC.insereCodigo(", ");
                    } else {
                        geradorC.insereCodigo(";\n");
                    }
                }
            } else {
                //caso o tipo passado for inválido, atribui tipo inteiro para a variável
                if (p instanceof PropriedadesVariavelOuConstante) {
                    ((PropriedadesVariavelOuConstante) p).setTipo(TipoBasico.INTEIRO);
                } else if (p instanceof PropriedadesVetor) {
                    ((PropriedadesVetor) p).setTipo(TipoBasico.INTEIRO);
                }
            }
        }

        //adiciona qualquer variável de registros existente na lista a ser retornada
        psbList1.addAll(psbList2);

        return psbList1;
    }

    /**
     * identificador : ('^')? IDENTIFICADOR ('.' IDENTIFICADOR)* dimensao* ;
     */
    public PropriedadesSimboloBasico identificador() {
        // ('^')?
        reconheceToken(TipoToken.SR_Circunflexo, false);
        String nomeIdent = reconheceToken(TipoToken.Identificador, true).getLexema();

        // ('.' IDENTIFICADOR)*
        while (tokenAtual.getTipo() == TipoToken.SR_Ponto) {
            obterProximo();
            nomeIdent += "." + reconheceToken(TipoToken.Identificador, true).getLexema();
        }

        PropriedadesSimboloBasico psb = null;

        // Obtém as propriedades da dimensão (constante)       
        PropriedadesVetor pv = new PropriedadesVetor();
        pv.setLinha(lexico.getLeitor().getLinha());
        pv = dimensao();

        //verifica se é vetor ou não e adiciona suas propriedades
        if (pv.getDimensoes() == 0) {
            psb = new PropriedadesVariavelOuConstante(nomeIdent, false);
            psb.setLinha(lexico.getLeitor().getLinha());
        } else {
            pv.setLexema(nomeIdent);
            psb = pv;
        }

        return psb;
    }

    /**
     * dimensao : ('[' exp_aritmetica ']')* ; .
     *
     * @return Retorna um objeto de propriedades vetor contendo a quantidade de
     * dimensões e a representação do tamanho de cada dimensão como Strings.
     */
    public PropriedadesVetor dimensao() {
        PropriedadesVetor pv = new PropriedadesVetor();
        int dimensoes = 0; //número de dimensões do vetor
        List<String> listaExp = new ArrayList<String>();

        while (tokenAtual.getTipo() == TipoToken.SR_AbreColchetes) {
            obterProximo();
            //recupera a expressão que representa o tamanho da dimensão
            PropriedadesVariavelOuConstante pvc = expAritmetica();
            listaExp.add(pvc.getValor()); //valor contém a representação em String do tamanho da dimensão
            reconheceToken(TipoToken.SR_FechaColchetes, true);
            dimensoes++;
        }

        pv.setDimensoes(dimensoes);
        pv.setTamanhoDimensoes(listaExp);

        return pv;
    }

    /**
     * tipo : 'registro' (variavel)+ 'fimregistro' | ('^')? (('literal' |
     * 'inteiro' | 'real' | 'logico') | IDENTIFICADOR) ;
     *
     * @return Retorna um objeto de propriedades de tipo, que podem ser tipo
     * básico, tipos redefinidos ou registros
     */
    public PropriedadesTipo tipo() {
        PropriedadesTipo ptr = null;
        switch (tokenAtual.getTipo()) {
            case PR_Registro: {
                obterProximo();
                geradorC.insereCodigo("struct {\n"); //insere declaração de registro em código C

                // Registro anônimo (ainda não declarado)
                ptr = new PropriedadesRegistro();

                // Obtém as propriedades de membros
                List<PropriedadesSimboloBasico> psbList = variavel();

                // Obtém as propriedades de membros
                while (tokenAtual.getTipo() == TipoToken.SR_Circunflexo
                        || tokenAtual.getTipo() == TipoToken.Identificador) {
                    psbList.addAll(variavel());
                }

                reconheceToken(TipoToken.PR_FimRegistro, true);
                ((PropriedadesRegistro) ptr).setSimbolos(psbList);

                geradorC.insereCodigo("}"); //termina o escopo da declaração de registro no código C

                break;
            }

            case SR_Circunflexo:
            case Identificador:
            case PR_Literal:
            case PR_Inteiro:
            case PR_Real:
            case PR_Logico: {
                Token t = reconheceToken(TipoToken.SR_Circunflexo, false);
                if (tokenAtual.getTipo() == TipoToken.Identificador) {
                    // Tipo não básico (registro, tipo redefinido) já declarado
                    PropriedadesSimboloBasico psb = semantico.buscaSimbolo(tokenAtual.getLexema());

                    if (psb != null
                            && (psb.getCategoria() == CategoriaSimbolo.Tipo
                            || psb.getCategoria() == CategoriaSimbolo.Registro)) {
                        ptr = (PropriedadesTipo) psb;

                        if (t != null) {
                            ptr.setPonteiro(true);
                        }

                        //insere no código em C o nome do tipo encontrado
                        geradorC.insereCodigo(ptr.getNome());
                    } else {
                        // Erro de tipo não declarado
                        semantico.erro(TipoErroSemantico.TipoNaoDeclarado,
                                lexico.getLeitor().getLinha(), tokenAtual.getLexema());
                    }

                    obterProximo();

                } else {
                    // Tipo básico
                    TipoToken[] tipos = {TipoToken.PR_Literal, TipoToken.PR_Inteiro,
                        TipoToken.PR_Real, TipoToken.PR_Logico};
                    String lex = reconheceToken(tipos, true).getLexema();
                    ptr = (PropriedadesTipo) semantico.buscaSimbolo(lex);

                    if (t != null) {
                        ptr.setPonteiro(true);
                    }

                    //insere no código em C o nome do tipo encontrado com as devidas conversões
                    String tipoC = GeradorCodigo.converteTipo(ptr.getNome(), false);
                    geradorC.insereCodigo(tipoC);
                }
                break;
            }

            default:
                erro();
        }
        return ptr;
    }

    /**
     * parametro : ('var')? identificador (',' identificador)* ':' ('^')?
     * (('literal' | 'inteiro' | 'real' | 'logico') | IDENTIFICADOR) (','
     * parametro)* ;
     *
     * @return Retorna uma lista de propriedades de variáveis dos parâmetros
     */
    public List<PropriedadesVariavelOuConstante> parametro() {
        List<PropriedadesVariavelOuConstante> pvcList = new ArrayList<PropriedadesVariavelOuConstante>();
        PropriedadesTipo pt = null;

        // Obtém as propriedades dos parâmetros
        reconheceToken(TipoToken.PR_Var, false);
        PropriedadesSimboloBasico psb = identificador();
        pvcList.add((PropriedadesVariavelOuConstante) psb);

        while (tokenAtual.getTipo() == TipoToken.SR_Virgula) {
            obterProximo();
            psb = identificador();
            pvcList.add((PropriedadesVariavelOuConstante) psb);
        }

        reconheceToken(TipoToken.SR_DoisPontos, true);
        Token pont = reconheceToken(TipoToken.SR_Circunflexo, false);

        // Obtém as propriedades do tipo
        if (tokenAtual.getTipo() == TipoToken.Identificador) {
            // Tipo não básico já declarado
            psb = semantico.buscaSimbolo(tokenAtual.getLexema());

            if (psb != null
                    && (psb.getCategoria() == CategoriaSimbolo.Tipo
                    || psb.getCategoria() == CategoriaSimbolo.Registro)) {
                pt = (PropriedadesTipo) psb;
            } else {
                // Erro de tipo não declarado
                semantico.erro(TipoErroSemantico.TipoNaoDeclarado,
                        lexico.getLeitor().getLinha(), tokenAtual.getLexema());
            }

            obterProximo();
        } else {
            // Tipo básico
            TipoToken[] tipos = {TipoToken.PR_Literal, TipoToken.PR_Inteiro,
                TipoToken.PR_Real, TipoToken.PR_Logico};
            String lex = reconheceToken(tipos, true).getLexema();
            pt = (PropriedadesTipo) semantico.buscaSimbolo(lex);
        }

        // Atribui os tipos
        if (pt != null) { // Será null se tiver dado erro de tipo inválido
            for (PropriedadesVariavelOuConstante pvc : pvcList) {
                pvc.setTipo(pt.getNome());

                //verifica se é ponteiro e atribui a string devida ao seu valor
                if (pont != null) {
                    pvc.setValor("*");
                } else {
                    pvc.setValor("");
                }

                //se for literal, então será um ponteiro para char
                if (pt.getNome().equals("literal")) {
                    pvc.setValor("*");
                }
            }
        } else { //atribui tipos inteiros como padrão
            for (PropriedadesVariavelOuConstante pvc : pvcList) {
                pvc.setTipo(TipoBasico.INTEIRO);

                if (pont != null) {
                    pvc.setValor("*");
                } else {
                    pvc.setValor("");
                }
            }
        }

        // Mais parâmetros
        if (tokenAtual.getTipo() == TipoToken.SR_Virgula) {
            obterProximo();
            List<PropriedadesVariavelOuConstante> pvcAddList = parametro();
            pvcList.addAll(pvcAddList);
        }

        return pvcList;
    }

    /**
     * cmd : 'leia' '(' identificador (',' identificador)* ')' | 'escreva' '('
     * expressao (',' expressao)* ')' | 'se' expressao 'entao' (cmd)* ('senao'
     * (cmd)*)? 'fimse' | 'caso' exp_aritmetica 'seja' (constantes ':' (cmd)*)+
     * ('senao' (cmd)*)? 'fimcaso' | 'para' IDENTIFICADOR '&lt;-' exp_aritmetica
     * 'ate' exp_aritmetica 'faca' (cmd)* 'fimpara' | 'enquanto' expressao
     * 'faca' (cmd)* 'fimenquanto' | 'faca' (cmd)* 'ate' expressao | '^'
     * IDENTIFICADOR ('.' IDENTIFICADOR)* dimensao '&lt;-' expressao |
     * IDENTIFICADOR chamada_atribuicao | 'retorne' expressao* ;
     */
    public void cmd() {
        switch (tokenAtual.getTipo()) {
            case PR_Leia: {
                obterProximo();

                geradorC.insereCodigo("scanf(\""); //insere código C para leitura de dados
                reconheceToken(TipoToken.SR_AbreParenteses, true);
                PropriedadesSimboloBasico psb = identificador();

                PropriedadesSimboloBasico psbA = semantico.buscaSimbolo(psb.getLexema());

                //verifica se o identificador achado está na tabela de símbolos
                if (psbA == null) {
                    semantico.erro(TipoErroSemantico.IdentificadorNaoDeclarado, psb.getLinha(), psb.getLexema());
                } else {

                    //verifica se o símbolo achado é vetor ou não para geração do código C
                    if (psbA instanceof PropriedadesVariavelOuConstante) {
                        PropriedadesVariavelOuConstante pvc = (PropriedadesVariavelOuConstante) psbA;

                        //converte o tipo do identificador para a formatação adequada na linguagem C
                        String tipoC = GeradorCodigo.converteTipo(pvc.getTipo(), true);

                        //verifica se a variável de leitura é uma string
                        if (pvc.getTipo().equals("literal")) {
                            //se for literal não precisa do endereço no scanf, pois é um vetor de chars
                            geradorC.insereCodigo(tipoC + "\", " + pvc.getLexema() + ");\n"); //insere o resto do código C para função scanf
                        } else {
                            geradorC.insereCodigo(tipoC + "\", &" + pvc.getLexema() + ");\n"); //insere o resto do código C para função scanf
                        }

                    } else {
                        PropriedadesVetor pv = (PropriedadesVetor) psbA;

                        //converte o tipo do identificador para a formatação adequada na linguagem C
                        String tipoC = GeradorCodigo.converteTipo(pv.getTipo(), true);

                        //verifica se a variável de leitura é uma string
                        if (pv.getTipo().equals("literal")) {
                            //se for literal não precisa do endereço no scanf, pois é um vetor de chars
                            geradorC.insereCodigo(tipoC + "\", " + pv.getLexema() + ");\n"); //insere o resto do código C para função scanf
                        } else {
                            geradorC.insereCodigo(tipoC + "\", &" + pv.getLexema() + ");\n"); //insere o resto do código C para função scanf
                        }

                        //insere as informações do vetor no código C
                        if (psb instanceof PropriedadesVetor) {
                            for (String exp : ((PropriedadesVetor) psb).getTamanhoDimensoes()) {
                                geradorC.insereCodigo("[");
                                geradorC.insereCodigo(exp);
                                geradorC.insereCodigo("]");
                            }
                        }

                        geradorC.insereCodigo(");\n"); //finaliza a função scanf
                    }
                }

                // (',' identificador)*
                while (tokenAtual.getTipo() == TipoToken.SR_Virgula) {
                    obterProximo();
                    psb = identificador();

                    geradorC.insereCodigo("scanf(\""); //insere código C para leitura de mais dados

                    psbA = semantico.buscaSimbolo(psb.getLexema());

                    //verifica se o identificador achado está na tabela de símbolos
                    if (psbA == null) {
                        semantico.erro(TipoErroSemantico.IdentificadorNaoDeclarado, psb.getLinha(), psb.getLexema());
                    } else {
                        if (psbA instanceof PropriedadesVariavelOuConstante) {
                            PropriedadesVariavelOuConstante pvc = (PropriedadesVariavelOuConstante) psbA;

                            String tipoC = GeradorCodigo.converteTipo(pvc.getTipo(), true);
                            geradorC.insereCodigo(tipoC + "\", &" + pvc.getLexema() + ");\n");

                        } else {
                            PropriedadesVetor pv = (PropriedadesVetor) psbA;

                            //converte o tipo do identificador para a formatação adequada na linguagem C
                            String tipoC = GeradorCodigo.converteTipo(pv.getTipo(), true);
                            geradorC.insereCodigo(tipoC + "\", &" + pv.getLexema()); //insere o resto do código C para função scanf

                            //insere as informações do vetor no código C
                            if (psb instanceof PropriedadesVetor) {
                                for (String exp : ((PropriedadesVetor) psb).getTamanhoDimensoes()) {
                                    geradorC.insereCodigo("[");
                                    geradorC.insereCodigo(exp);
                                    geradorC.insereCodigo("]");
                                }
                            }

                            geradorC.insereCodigo(");\n"); //finaliza a função scanf
                        }
                    }
                }

                reconheceToken(TipoToken.SR_FechaParenteses, true);
                break;
            }

            case PR_Escreva:
                obterProximo();
                geradorC.insereCodigo("printf(\""); //inicia função printf para escrita de dados no código C
                reconheceToken(TipoToken.SR_AbreParenteses, true);
                PropriedadesVariavelOuConstante expressao = expressao();

                //monta a formatação do printf e sua expressão
                String tipoC = GeradorCodigo.converteTipo(expressao.getTipo(), true);
                geradorC.insereCodigo(tipoC + "\", " + expressao.getValor() + ");\n");

                while (tokenAtual.getTipo() == TipoToken.SR_Virgula) {
                    obterProximo();

                    geradorC.insereCodigo("printf(\""); //inicia função printf para escrita de mais dados
                    expressao = expressao();

                    //monta a formatação do printf e sua expressão
                    tipoC = GeradorCodigo.converteTipo(expressao.getTipo(), true);
                    geradorC.insereCodigo(tipoC + "\", " + expressao.getValor() + ");\n");
                }

                reconheceToken(TipoToken.SR_FechaParenteses, true);

                break;

            case PR_Se: {
                obterProximo();
                String exp = expressao().getValor();
                reconheceToken(TipoToken.PR_Entao, true);

                //monta o código do if em C com o valor encontrado em expressão
                geradorC.insereCodigo("if (" + exp + ") {\n");

                while (tokenAtual.getTipo() == TipoToken.PR_Leia
                        || tokenAtual.getTipo() == TipoToken.PR_Escreva
                        || tokenAtual.getTipo() == TipoToken.PR_Se
                        || tokenAtual.getTipo() == TipoToken.PR_Caso
                        || tokenAtual.getTipo() == TipoToken.PR_Para
                        || tokenAtual.getTipo() == TipoToken.PR_Enquanto
                        || tokenAtual.getTipo() == TipoToken.PR_Faca
                        || tokenAtual.getTipo() == TipoToken.SR_Circunflexo
                        || tokenAtual.getTipo() == TipoToken.Identificador
                        || tokenAtual.getTipo() == TipoToken.PR_Retorne) {
                    // verifica se comando 'retorne' não é permitido neste escopo
                    if (tokenAtual.getTipo() == TipoToken.PR_Retorne && !semantico.isFuncao()) {
                        semantico.erro(TipoErroSemantico.ComandoRetorneNaoPermitido, lexico.getLeitor().getLinha(), null);
                    }
                    cmd();
                }

                if (tokenAtual.getTipo() == TipoToken.PR_Senao) {
                    obterProximo();

                    //insere o código da parte do else terminando o escopo do if
                    geradorC.insereCodigo("} else {\n");

                    while (tokenAtual.getTipo() == TipoToken.PR_Leia
                            || tokenAtual.getTipo() == TipoToken.PR_Escreva
                            || tokenAtual.getTipo() == TipoToken.PR_Se
                            || tokenAtual.getTipo() == TipoToken.PR_Caso
                            || tokenAtual.getTipo() == TipoToken.PR_Para
                            || tokenAtual.getTipo() == TipoToken.PR_Enquanto
                            || tokenAtual.getTipo() == TipoToken.PR_Faca
                            || tokenAtual.getTipo() == TipoToken.SR_Circunflexo
                            || tokenAtual.getTipo() == TipoToken.Identificador
                            || tokenAtual.getTipo() == TipoToken.PR_Retorne) {
                        // verifica se comando 'retorne' não é permitido neste escopo
                        if (tokenAtual.getTipo() == TipoToken.PR_Retorne && !semantico.isFuncao()) {
                            semantico.erro(TipoErroSemantico.ComandoRetorneNaoPermitido, lexico.getLeitor().getLinha(), null);
                        }
                        cmd();
                    }
                }

                reconheceToken(TipoToken.PR_FimSe, true);
                geradorC.insereCodigo("}\n"); //termina o escopo do comando if/else
                break;
            }

            case PR_Caso:
                obterProximo();
                geradorC.insereCodigo("switch ("); //inicia código C de um switch
                PropriedadesVariavelOuConstante expA = expAritmetica();
                geradorC.insereCodigo(expA.getValor() + ") {\n"); //inicia a seção de cases do código C
                reconheceToken(TipoToken.PR_Seja, true);
                int[] constante = constantes(); //recupera o valor das constantes do case

                //verifica se as constantes estão de acordo
                if (constante[0] > constante[1]) {
                    constante[1] = constante[0];
                }

                //insere todos os cases necessários recuperados pelas constantes
                for (int i = constante[0]; i < (constante[1] + 1); i++) {
                    geradorC.insereCodigo("case " + i + ":\n");
                }

                reconheceToken(TipoToken.SR_DoisPontos, true);

                while (tokenAtual.getTipo() == TipoToken.PR_Leia
                        || tokenAtual.getTipo() == TipoToken.PR_Escreva
                        || tokenAtual.getTipo() == TipoToken.PR_Se
                        || tokenAtual.getTipo() == TipoToken.PR_Caso
                        || tokenAtual.getTipo() == TipoToken.PR_Para
                        || tokenAtual.getTipo() == TipoToken.PR_Enquanto
                        || tokenAtual.getTipo() == TipoToken.PR_Faca
                        || tokenAtual.getTipo() == TipoToken.SR_Circunflexo
                        || tokenAtual.getTipo() == TipoToken.Identificador
                        || tokenAtual.getTipo() == TipoToken.PR_Retorne) {
                    // verifica se comando 'retorne' não é permitido neste escopo
                    if (tokenAtual.getTipo() == TipoToken.PR_Retorne && !semantico.isFuncao()) {
                        semantico.erro(TipoErroSemantico.ComandoRetorneNaoPermitido, lexico.getLeitor().getLinha(), null);
                    }
                    cmd();
                }

                geradorC.insereCodigo("break;\n"); //insere o break do case

                while (tokenAtual.getTipo() == TipoToken.SR_Subtracao
                        || tokenAtual.getTipo() == TipoToken.NumeroInteiro) {
                    constante = constantes(); //

                    //verifica se as constantes estão de acordo
                    if (constante[0] > constante[1]) {
                        constante[1] = constante[0];
                    }

                    //insere todos os cases necessários recuperados pelas constantes
                    for (int i = constante[0]; i < (constante[1] + 1); i++) {
                        geradorC.insereCodigo("case " + i + ":\n");
                    }

                    reconheceToken(TipoToken.SR_DoisPontos, true);
                    while (tokenAtual.getTipo() == TipoToken.PR_Leia
                            || tokenAtual.getTipo() == TipoToken.PR_Escreva
                            || tokenAtual.getTipo() == TipoToken.PR_Se
                            || tokenAtual.getTipo() == TipoToken.PR_Caso
                            || tokenAtual.getTipo() == TipoToken.PR_Para
                            || tokenAtual.getTipo() == TipoToken.PR_Enquanto
                            || tokenAtual.getTipo() == TipoToken.PR_Faca
                            || tokenAtual.getTipo() == TipoToken.SR_Circunflexo
                            || tokenAtual.getTipo() == TipoToken.Identificador
                            || tokenAtual.getTipo() == TipoToken.PR_Retorne) {
                        // verifica se comando 'retorne' não é permitido neste escopo
                        if (tokenAtual.getTipo() == TipoToken.PR_Retorne && !semantico.isFuncao()) {
                            semantico.erro(TipoErroSemantico.ComandoRetorneNaoPermitido, lexico.getLeitor().getLinha(), null);
                        }
                        cmd();
                    }

                    geradorC.insereCodigo("break;\n"); //insere o break do case
                }

                if (tokenAtual.getTipo() == TipoToken.PR_Senao) {
                    obterProximo();
                    geradorC.insereCodigo("default:\n"); //insere o código default

                    while (tokenAtual.getTipo() == TipoToken.PR_Leia
                            || tokenAtual.getTipo() == TipoToken.PR_Escreva
                            || tokenAtual.getTipo() == TipoToken.PR_Se
                            || tokenAtual.getTipo() == TipoToken.PR_Caso
                            || tokenAtual.getTipo() == TipoToken.PR_Para
                            || tokenAtual.getTipo() == TipoToken.PR_Enquanto
                            || tokenAtual.getTipo() == TipoToken.PR_Faca
                            || tokenAtual.getTipo() == TipoToken.SR_Circunflexo
                            || tokenAtual.getTipo() == TipoToken.Identificador
                            || tokenAtual.getTipo() == TipoToken.PR_Retorne) {
                        // verifica se comando 'retorne' não é permitido neste escopo
                        if (tokenAtual.getTipo() == TipoToken.PR_Retorne && !semantico.isFuncao()) {
                            semantico.erro(TipoErroSemantico.ComandoRetorneNaoPermitido, lexico.getLeitor().getLinha(), null);
                        }
                        cmd();
                    }
                }

                reconheceToken(TipoToken.PR_FimCaso, true);
                geradorC.insereCodigo("}\n"); //encerra o escopo do switch
                break;

            case PR_Para: {
                obterProximo();

                Token ident = reconheceToken(TipoToken.Identificador, true);

                //verifica se o identificador utilizado no comando faça foi declarado
                if (semantico.buscaSimbolo(ident.getLexema()) == null) {
                    semantico.erro(TipoErroSemantico.IdentificadorNaoDeclarado, lexico.getLeitor().getLinha(), ident.getLexema());
                }

                reconheceToken(TipoToken.SR_Atribuicao, true);

                //recupera o intervalo inicial do comando faça para criação do código C
                String intInicial = expAritmetica().getValor();

                reconheceToken(TipoToken.PR_Ate, true);

                //recupera o intervalo final do comando faça para criação do código C
                String intFinal = expAritmetica().getValor();
                reconheceToken(TipoToken.PR_Faca, true);

                //insere o código do comando for na linguagem C
                geradorC.insereCodigo("for (" + ident.getLexema() + " = " + intInicial + "; ");
                geradorC.insereCodigo(ident.getLexema() + " <= " + intFinal + "; ");
                geradorC.insereCodigo(ident.getLexema() + "++) {\n");

                while (tokenAtual.getTipo() == TipoToken.PR_Leia
                        || tokenAtual.getTipo() == TipoToken.PR_Escreva
                        || tokenAtual.getTipo() == TipoToken.PR_Se
                        || tokenAtual.getTipo() == TipoToken.PR_Caso
                        || tokenAtual.getTipo() == TipoToken.PR_Para
                        || tokenAtual.getTipo() == TipoToken.PR_Enquanto
                        || tokenAtual.getTipo() == TipoToken.PR_Faca
                        || tokenAtual.getTipo() == TipoToken.SR_Circunflexo
                        || tokenAtual.getTipo() == TipoToken.Identificador
                        || tokenAtual.getTipo() == TipoToken.PR_Retorne) {
                    //  verifica se comando 'retorne' não é permitido neste escopo
                    if (tokenAtual.getTipo() == TipoToken.PR_Retorne && !semantico.isFuncao()) {
                        semantico.erro(TipoErroSemantico.ComandoRetorneNaoPermitido, lexico.getLeitor().getLinha(), null);
                    }
                    cmd();
                }

                reconheceToken(TipoToken.PR_FimPara, true);
                geradorC.insereCodigo("}\n"); //termina o escopo do comando for               
                break;
            }

            case PR_Enquanto: {
                obterProximo();
                String exp = expressao().getValor();
                reconheceToken(TipoToken.PR_Faca, true);

                //insere o código para while
                geradorC.insereCodigo("while(" + exp + ") {\n");

                while (tokenAtual.getTipo() == TipoToken.PR_Leia
                        || tokenAtual.getTipo() == TipoToken.PR_Escreva
                        || tokenAtual.getTipo() == TipoToken.PR_Se
                        || tokenAtual.getTipo() == TipoToken.PR_Caso
                        || tokenAtual.getTipo() == TipoToken.PR_Para
                        || tokenAtual.getTipo() == TipoToken.PR_Enquanto
                        || tokenAtual.getTipo() == TipoToken.PR_Faca
                        || tokenAtual.getTipo() == TipoToken.SR_Circunflexo
                        || tokenAtual.getTipo() == TipoToken.Identificador
                        || tokenAtual.getTipo() == TipoToken.PR_Retorne) {
                    // verifica se comando 'retorne' não é permitido neste escopo
                    if (tokenAtual.getTipo() == TipoToken.PR_Retorne && !semantico.isFuncao()) {
                        semantico.erro(TipoErroSemantico.ComandoRetorneNaoPermitido, lexico.getLeitor().getLinha(), null);
                    }
                    cmd();
                }

                reconheceToken(TipoToken.PR_FimEnquanto, true);
                geradorC.insereCodigo("}\n"); //encerra o escopo do comando while

                break;
            }

            case PR_Faca: {
                obterProximo();

                //insere o código para o comando do
                geradorC.insereCodigo("do {\n");

                while (tokenAtual.getTipo() == TipoToken.PR_Leia
                        || tokenAtual.getTipo() == TipoToken.PR_Escreva
                        || tokenAtual.getTipo() == TipoToken.PR_Se
                        || tokenAtual.getTipo() == TipoToken.PR_Caso
                        || tokenAtual.getTipo() == TipoToken.PR_Para
                        || tokenAtual.getTipo() == TipoToken.PR_Enquanto
                        || tokenAtual.getTipo() == TipoToken.PR_Faca
                        || tokenAtual.getTipo() == TipoToken.SR_Circunflexo
                        || tokenAtual.getTipo() == TipoToken.Identificador
                        || tokenAtual.getTipo() == TipoToken.PR_Retorne) {
                    // verifica se comando 'retorne' não é permitido neste escopo
                    if (tokenAtual.getTipo() == TipoToken.PR_Retorne && !semantico.isFuncao()) {
                        semantico.erro(TipoErroSemantico.ComandoRetorneNaoPermitido, lexico.getLeitor().getLinha(), null);
                    }
                    cmd();
                }

                reconheceToken(TipoToken.PR_Ate, true);
                String exp = expressao().getValor();

                //monta o código para a condição do comando do e encerra seu escopo
                geradorC.insereCodigo("} while (!(" + exp + "));\n");

                break;
            }

            case SR_Circunflexo: {
                int linha = lexico.getLeitor().getLinha();

                //representação de toda cadeia encontrada para mostrar no erro caso ocorra
                StringBuilder todaCadeia = new StringBuilder();

                //representação o lexema para busca na tabela de símbolos
                StringBuilder lex = new StringBuilder();

                obterProximo();
                Token ident = reconheceToken(TipoToken.Identificador, true);
                todaCadeia.append("^");
                lex.append(ident.getLexema());

                //caso seja um registro, monta o lexema de acordo
                while (tokenAtual.getTipo() == TipoToken.SR_Ponto) {
                    obterProximo();
                    ident = reconheceToken(TipoToken.Identificador, true);
                    lex.append(ident.getLexema());
                }

                todaCadeia.append(lex);

                //verifica se é um vetor, e atribui a todaCadeia sua representação
                PropriedadesVetor pv = dimensao();

                for (String exp : pv.getTamanhoDimensoes()) {
                    todaCadeia.append("[");
                    todaCadeia.append(exp);
                    todaCadeia.append("]");
                }

                //busca na tabela de símbolos utilizando o lexema montado acima
                PropriedadesSimboloBasico psb = semantico.buscaSimbolo(lex.toString());

                //verificação de atribuição não compatível e se identificador está na tabela de símbolos
                if (psb == null) {
                    semantico.erro(TipoErroSemantico.IdentificadorNaoDeclarado, linha, todaCadeia.toString());
                    reconheceToken(TipoToken.SR_Atribuicao, true);
                    expressao();
                } else {
                    //cria uma cadeia para representação na linguagem C de ponteiro
                    String stringC = todaCadeia.toString();

                    //modifica a notação de ponteiro para a linguagem C                    
                    stringC = stringC.replace('^', '*');

                    //verifica se é um vetor
                    if (pv.getDimensoes() > 0) {
                        PropriedadesVetor pv2 = (PropriedadesVetor) psb;
                        reconheceToken(TipoToken.SR_Atribuicao, true);
                        PropriedadesVariavelOuConstante pvc = expressao();

                        //insere o código da atribuição no código C
                        //verifica se a expressão atribuida é literal para uso da função strcpy
                        if (pvc.getTipo().equals("literal")) {
                            geradorC.insereCodigo("strcpy(" + stringC + ", " + pvc.getValor() + ");\n");
                        } else {
                            geradorC.insereCodigo(stringC + " = " + pvc.getValor() + ";\n");
                        }

                        //verifica o tipo retornado do método expressão para mostrar erros de atribuição não compatível
                        if ((!pvc.getTipo().equals(pv2.getTipo())) && (!(pvc.getTipo().equals("real") && pv2.getTipo().equals("inteiro")))) {
                            semantico.erro(TipoErroSemantico.AtribuicaoNaoCompativel, linha, todaCadeia.toString());
                        }
                    } else {
                        PropriedadesVariavelOuConstante pvc = (PropriedadesVariavelOuConstante) psb;
                        reconheceToken(TipoToken.SR_Atribuicao, true);
                        PropriedadesVariavelOuConstante pvc2 = expressao();

                        //insere o código da atribuição no código C
                        //verifica se a expressão atribuida é literal para uso da função strcpy
                        if (pvc2.getTipo().equals("literal")) {
                            geradorC.insereCodigo("strcpy(" + stringC + ", " + pvc2.getValor() + ");\n");
                        } else {
                            geradorC.insereCodigo(stringC + " = " + pvc2.getValor() + ";\n");
                        }

                        //verifica o tipo retornado do método expressão para mostrar erros de atribuição não compatível
                        if ((!pvc.getTipo().equals(pvc2.getTipo())) && (!(pvc.getTipo().equals("real") && pvc2.getTipo().equals("inteiro")))) {
                            semantico.erro(TipoErroSemantico.AtribuicaoNaoCompativel, linha, todaCadeia.toString());
                        }
                    }
                }

                break;
            }

            case Identificador:
                String ident = tokenAtual.getLexema();
                obterProximo();
                chamadaAtribuicao(ident);
                break;

            case PR_Retorne:
                geradorC.insereCodigo("return "); //insere o código C para o retorno                
                obterProximo();
                PropriedadesVariavelOuConstante exp = expressao();

                //insere o valor do retorno ao código C
                geradorC.insereCodigo(exp.getValor() + ";\n");
                break;

            default:
                erro();
        }
    }

    /**
     * chamada_atribuicao : '(' expressao (',' expressao)* ')' | ('.'
     * IDENTIFICADOR)* dimensao '&lt;-' expressao ;
     *
     * @param ident Identificador que da início a cadeia do lexema, no qual será
     * utilizado para buscas na tabela de símbolos.
     */
    public void chamadaAtribuicao(String ident) {
        StringBuilder lex = new StringBuilder();
        StringBuilder todaCadeia = new StringBuilder();
        int qParam = 0;
        int linha = lexico.getLeitor().getLinha();

        switch (tokenAtual.getTipo()) {
            case SR_AbreParenteses: {
                List<String> paramsT = new ArrayList<String>(); //guarda tipos dos parâmetros da chamada
                todaCadeia.append(ident).append("(");
                obterProximo();
                //obtém as propriedades das expressões
                PropriedadesVariavelOuConstante pvc = expressao();
                paramsT.add(pvc.getTipo());
                if (!pvc.getValor().equals("")) {
                    //caso exista uma expressão não vazia, aumenta a quantidade de parâmetros
                    qParam++;
                }

                todaCadeia.append(pvc.getValor());

                //verifica se existem mais parâmetros
                while (tokenAtual.getTipo() == TipoToken.SR_Virgula) {
                    obterProximo();
                    pvc = expressao();
                    todaCadeia.append(", ");
                    todaCadeia.append(pvc.getValor());
                    paramsT.add(pvc.getTipo());
                    qParam++;
                }

                reconheceToken(TipoToken.SR_FechaParenteses, true);

                todaCadeia.append(")");

                //insere a cadeia da chamada do procedimento no código C
                geradorC.insereCodigo(todaCadeia + ";\n");

                //verifica se o identificador está na tabela de símbolos
                PropriedadesSimboloBasico psb = semantico.buscaSimbolo(ident);

                if (psb == null) {
                    semantico.erro(TipoErroSemantico.IdentificadorNaoDeclarado, linha, ident);
                } else {
                    //verifica compatibilidade dos parâmetros do procedimento
                    if (psb instanceof PropriedadesProcedimento) {
                        PropriedadesProcedimento pp = (PropriedadesProcedimento) psb;

                        if (pp.getQtdeParametros() != qParam) {
                            semantico.erro(TipoErroSemantico.IncompatibilidadeParametros, linha, ident);
                        } else {
                            //verifica os tipos presentes na chamada com os tipos presentes na propriedades de procedimento
                            //caso exista diferença, lança erro de incompatibilidade de parametros
                            for (int i = 0; i < pp.getQtdeParametros(); i++) {
                                if (!paramsT.get(i).equals(pp.getTiposParametros().get(i))) {
                                    semantico.erro(TipoErroSemantico.IncompatibilidadeParametros, linha, ident);
                                }
                            }
                        }
                    }
                }

                break;
            }

            case SR_Ponto:
            case SR_AbreColchetes:
            case SR_Atribuicao:
                lex.append(ident); //constroi o lexema do identificador

                //caso seja registro, continua construindo lexema
                while (tokenAtual.getTipo() == TipoToken.SR_Ponto) {
                    obterProximo();
                    Token t = reconheceToken(TipoToken.Identificador, true);
                    lex.append(".");
                    lex.append(t.getLexema());
                }

                todaCadeia.append(lex); //toda cadeia recebe lexema

                PropriedadesVetor pv = dimensao(); //verifica propriedades de vetor

                //verifica se é vetor. se for, adiciona suas propriedades na cadeia completa
                if (pv.getDimensoes() > 0) {
                    for (String exp : pv.getTamanhoDimensoes()) {
                        todaCadeia.append("[");
                        todaCadeia.append(exp);
                        todaCadeia.append("]");
                    }
                }

                //busca na tabela de símbolos utilizando o lexema construido acima
                PropriedadesSimboloBasico psb = semantico.buscaSimbolo(lex.toString());

                //verifica a existência do símbolo na tabela
                if (psb == null) {
                    semantico.erro(TipoErroSemantico.IdentificadorNaoDeclarado, linha, todaCadeia.toString());
                    reconheceToken(TipoToken.SR_Atribuicao, true);
                    expressao();
                } else {

                    //verifica a incompatibilidade de atribuição
                    if (pv.getDimensoes() > 0) {
                        PropriedadesVetor pv2 = (PropriedadesVetor) psb;
                        reconheceToken(TipoToken.SR_Atribuicao, true);
                        PropriedadesVariavelOuConstante pvc = expressao();

                        //insere o código da atribuição no código C
                        //verifica se a expressão atribuida é literal para uso da função strcpy
                        if (pvc.getTipo().equals("literal")) {
                            geradorC.insereCodigo("strcpy(" + todaCadeia + ", " + pvc.getValor() + ");\n");
                        } else {
                            geradorC.insereCodigo(todaCadeia + " = " + pvc.getValor() + ";\n");
                        }

                        //verifica o tipo retornado pela expressão para decidir sobre incompatibilidade de atribuição
                        if ((!pv2.getTipo().equals(pvc.getTipo()))
                                && (!((pv2.getTipo().equals("real") && pvc.getTipo().equals("inteiro"))
                                || (pvc.getTipo().equals("real") && pv2.getTipo().equals("inteiro"))))) {
                            semantico.erro(TipoErroSemantico.AtribuicaoNaoCompativel, linha, todaCadeia.toString());
                        }
                    } else {
                        PropriedadesVariavelOuConstante pvc = (PropriedadesVariavelOuConstante) psb;
                        reconheceToken(TipoToken.SR_Atribuicao, true);
                        PropriedadesVariavelOuConstante pvc2 = expressao();

                        //insere o código da atribuição no código C
                        //verifica se a expressão atribuida é literal para uso da função strcpy
                        if (pvc2.getTipo().equals("literal")) {
                            geradorC.insereCodigo("strcpy(" + todaCadeia + ", " + pvc2.getValor() + ");\n");
                        } else {
                            geradorC.insereCodigo(todaCadeia + " = " + pvc2.getValor() + ";\n");
                        }

                        //verifica o tipo retornado pela expressão para decidir sobre incompatibilidade de atribuição
                        if ((!pvc.getTipo().equals(pvc2.getTipo()))
                                && (!((pvc.getTipo().equals("real") && pvc2.getTipo().equals("inteiro"))
                                || (pvc2.getTipo().equals("real") && pvc.getTipo().equals("inteiro"))))) {
                            semantico.erro(TipoErroSemantico.AtribuicaoNaoCompativel, linha, todaCadeia.toString());
                        }
                    }
                }

                break;
            default:
                erro();
        }
    }

    /**
     * constantes : ('-')? NUMERO_INTEIRO ('..' ('-')? NUMERO_INTEIRO)* (','
     * constantes)? ;
     *
     * @return Retorna um vetor com dois inteiros, que representam o intervalo
     * representado por essa regra.
     */
    public int[] constantes() {
        int[] resultado = new int[2]; //vetor de inteiros que representa o intervalo de inteiros desta regra
        StringBuilder num = new StringBuilder(); //variável utilizada para recuperar os números constantes

        Token sub = reconheceToken(TipoToken.SR_Subtracao, false);
        Token inteiro = reconheceToken(TipoToken.NumeroInteiro, true);

        //verifica se o número é negativo para inserir em sua representação
        if (sub != null) {
            num.append("-");
        }

        //recupera a representação do número inteiro
        num.append(inteiro.getLexema());

        //atribui à primeiro posição do vetor de inteiros a representação inteira da string montada
        resultado[0] = Integer.parseInt(num.toString());

        //se não existe intervalo, atribui a segunda posição do vetor de inteiros igual a primeira
        if (tokenAtual.getTipo() != TipoToken.SR_Concatenacao) {
            resultado[1] = resultado[0];
        }

        //verifica o resto do intervalo se existir
        while (tokenAtual.getTipo() == TipoToken.SR_Concatenacao) {
            num = new StringBuilder(); //zera a string que representa o número
            obterProximo();

            sub = reconheceToken(TipoToken.SR_Subtracao, false);
            inteiro = reconheceToken(TipoToken.NumeroInteiro, true);

            if (sub != null) {
                num.append("-");
            }

            num.append(inteiro.getLexema());

            //atribui o número encontrado a segunda posição do vetor de inteiros
            resultado[1] = Integer.parseInt(num.toString());
        }

        if (tokenAtual.getTipo() == TipoToken.SR_Virgula) {
            obterProximo();
            constantes();
        }

        return resultado;
    }

    /**
     * exp_aritmetica : termo (('+'|'-') termo)* ;
     *
     * @return Retorna objeto de variável, que irá conter no campo valor a
     * representação como String da expressão aritmetica e no campo tipo o tipo
     * da expressão, levando em consideração as conversões de tipo que ocorrem
     * no decorrer dos retornos das pilha de chamada
     */
    public PropriedadesVariavelOuConstante expAritmetica() {
        //termo1 recebe a concatenação de todo o resto da pilha de chamada
        PropriedadesVariavelOuConstante termo1 = termo(); //inicio da pilha de chamada para expressão aritmetica

        //verifica se existem mais termos
        while (tokenAtual.getTipo() == TipoToken.SR_Adicao
                || tokenAtual.getTipo() == TipoToken.SR_Subtracao) {
            TipoToken[] tipos = {TipoToken.SR_Adicao, TipoToken.SR_Subtracao};
            Token operacao = reconheceToken(tipos, true);
            PropriedadesVariavelOuConstante termo2 = termo(); //segundo termo

            //constroi o lexema da expressão inteira
            termo1.setValor(termo1.getValor() + " " + operacao.getLexema() + " " + termo2.getValor());

            //realiza conversões de tipo para concatenação
            if (termo1.getTipo().equals("inteiro") && termo2.getTipo().equals("real")) {
                termo1.setTipo("real");
            } else if (!termo1.getTipo().equals(termo2.getTipo())) {
                termo1.setTipo(termo2.getTipo());
            }
        }

        return termo1;
    }

    /**
     * termo : fator (('*'|'/') fator)* ;
     *
     * @return Retorna objeto de variável, que irá conter no campo valor a
     * representação como String do termo e no campo tipo o tipo do termo,
     * levando em consideração as conversões de tipo que ocorrem no decorrer dos
     * retornos da pilha de chamadas
     */
    public PropriedadesVariavelOuConstante termo() {
        PropriedadesVariavelOuConstante fator1 = fator(); //continua pilha de chamadas

        //verifica se existe outro fator
        while (tokenAtual.getTipo() == TipoToken.SR_Multiplicacao
                || tokenAtual.getTipo() == TipoToken.SR_Divisao) {
            TipoToken[] tipos = {TipoToken.SR_Multiplicacao, TipoToken.SR_Divisao};
            Token operacao = reconheceToken(tipos, true);
            PropriedadesVariavelOuConstante fator2 = fator(); //recupera próximo fator           

            //vai construindo o lexema da expressão inteira
            fator1.setValor(fator1.getValor() + " " + operacao.getLexema() + " " + fator2.getValor());

            //faz conversões de tipo da multiplicação
            if (fator1.getTipo().equals("inteiro") && fator2.getTipo().equals("real")) {
                fator1.setTipo("real");
            }
        }

        return fator1;
    }

    /**
     * fator : parcela ('%' parcela)* ;
     *
     * @return Retorna objeto de variável, que irá conter no campo valor a
     * representação como String do fator e no campo tipo o tipo ddo fator,
     * levando em consideração as conversões de tipo que ocorrem no decorrer dos
     * retornos da pilha de chamadas
     */
    public PropriedadesVariavelOuConstante fator() {
        PropriedadesVariavelOuConstante parcela1 = parcela(); //continua pilha de chamadas

        //verifica se existem operações de módulo
        while (tokenAtual.getTipo() == TipoToken.SR_Modulo) {
            reconheceToken(TipoToken.SR_Modulo, true);
            PropriedadesVariavelOuConstante parcela2 = parcela();

            parcela1.setValor(parcela1.getValor() + " % " + parcela2.getValor());
        }

        return parcela1;
    }

    /**
     * parcela : ('-')? parcela_unario | parcela_nao_unario ;
     *
     * @return Retorna objeto de variável, que irá conter no campo valor a
     * representação como String da parcela e no campo tipo o tipo da parcela,
     * levando em consideração as conversões de tipo que ocorrem no decorrer dos
     * retornos da pilha de chamadas
     */
    public PropriedadesVariavelOuConstante parcela() {
        PropriedadesVariavelOuConstante resultado = new PropriedadesVariavelOuConstante("", false);

        switch (tokenAtual.getTipo()) {
            case SR_Subtracao:
            case SR_Circunflexo:
            case Identificador:
            case NumeroInteiro:
            case NumeroReal:
            case SR_AbreParenteses:
                Token opU = reconheceToken(TipoToken.SR_Subtracao, false);
                resultado = parcelaUnario();

                if (opU != null) {
                    resultado.setValor("-" + resultado.getValor());
                }

                break;

            case SR_Endereco:
            case CadeiaLiteral:
                resultado = parcelaNaoUnario();
                break;

            default:
                erro();
        }

        return resultado;
    }

    /**
     * parcela_unario : '^' IDENTIFICADOR ('.' IDENTIFICADOR)* dimensao |
     * IDENTIFICADOR chamada_partes | NUMERO_INTEIRO | NUMERO_REAL | '('
     * expressao ')' ;
     *
     * @return Retorna objeto de variável que contém no campo valor a String que
     * representa o lexema encontrado. O tipo pode ser inteiro, real, ou
     * dependente o tipo de um identificador ou retorno de uma função dependendo
     * do caminho seguido na análise no momento.
     */
    public PropriedadesVariavelOuConstante parcelaUnario() {
        PropriedadesVariavelOuConstante resultado = new PropriedadesVariavelOuConstante("", false);
        Token ident = null;
        int linha = lexico.getLeitor().getLinha();
        StringBuilder lex = new StringBuilder();
        StringBuilder todaParcela = new StringBuilder();

        switch (tokenAtual.getTipo()) {
            case SR_Circunflexo: {
                todaParcela.append("*"); //constroi a cadeia inteira para mostrar no erro caso ocorra
                obterProximo();
                ident = reconheceToken(TipoToken.Identificador, true);
                lex.append(ident.getLexema()); //controi o lexema para busca na tabela de símbolos

                //('.' IDENTIFICADOR)*
                //continua construindo o lexema
                while (tokenAtual.getTipo() == TipoToken.SR_Ponto) {
                    reconheceToken(TipoToken.SR_Ponto, true);
                    ident = reconheceToken(TipoToken.Identificador, true);
                    lex.append(".");
                    lex.append(ident.getLexema());
                }

                todaParcela.append(lex);

                PropriedadesVetor pv = dimensao();

                //verifica propriedades de vetor e constroi a cadeia completa de acordo
                if (pv.getDimensoes() > 0) {
                    for (String exp : pv.getTamanhoDimensoes()) {
                        todaParcela.append("[");
                        todaParcela.append(exp);
                        todaParcela.append("]");
                    }
                }

                //busca na tabela pelo lexema construido 
                PropriedadesSimboloBasico psb = semantico.buscaSimbolo(lex.toString());

                //verifica se o lexema construido já foi declarado
                if (psb == null) {
                    semantico.erro(TipoErroSemantico.IdentificadorNaoDeclarado, linha, todaParcela.toString());
                    resultado.setTipo(TipoBasico.INTEIRO);
                    resultado.setValor(todaParcela.toString());
                } else {
                    resultado.setValor(todaParcela.toString());

                    //recupera a propriedade de tipo do objeto retornado na busca acima
                    //atribui o tipo encontrado para o resultado que será retornado no método
                    if (pv.getDimensoes() > 0) {
                        PropriedadesVetor pv2 = (PropriedadesVetor) psb;
                        resultado.setTipo(pv2.getTipo());
                    } else {
                        PropriedadesVariavelOuConstante pvc = (PropriedadesVariavelOuConstante) psb;
                        resultado.setTipo(pvc.getTipo());
                    }
                }

                break;
            }

            case Identificador:
                lex.append(tokenAtual.getLexema());
                obterProximo();
                //verifica se ocorreu uma chamada de função 
                PropriedadesFuncao pf = chamadaPartes();
                todaParcela.append(lex).append(pf.getLexema());

                //verifica se é uma função que ocorreu, ou uma variável
                if (pf.getCategoria() == CategoriaSimbolo.Funcao) {
                    PropriedadesSimboloBasico psb = semantico.buscaSimbolo(lex.toString());

                    if (psb == null) {
                        semantico.erro(TipoErroSemantico.IdentificadorNaoDeclarado, linha, lex.toString());
                        resultado.setTipo(TipoBasico.INTEIRO);
                    } else {
                        resultado.setTipo(pf.getTipoRetorno());

                        PropriedadesFuncao pf2 = null;

                        //tenta recuperar as propriedades de função
                        if (psb instanceof PropriedadesFuncao) {
                            pf2 = (PropriedadesFuncao) psb;
                        }

                        resultado.setValor(todaParcela.toString());

                        //verifica incompatibilidade de parâmetros
                        if (pf2 != null) {
                            //atribui o tipo de retorno da função ao tipo do resultado que será retornado no método
                            resultado.setTipo(pf2.getTipoRetorno());

                            if (pf.getQtdeParametros() != pf2.getQtdeParametros()) {
                                semantico.erro(TipoErroSemantico.IncompatibilidadeParametros, linha, lex.toString());
                            } else {
                                //verifica os tipos dos parâmetros recuperados da tabela e os encontrados na chamada
                                for (int i = 0; i < pf.getQtdeParametros(); i++) {
                                    if (!pf.getTiposParametros().get(i).equals(pf2.getTiposParametros().get(i))) {
                                        semantico.erro(TipoErroSemantico.IncompatibilidadeParametros, linha, lex.toString());
                                    }
                                }
                            }
                        }
                    }
                } else {

                    //é uma variável
                    String identT = todaParcela.toString();

                    /* 
                     * se a expressão retornada contiver propriedades de vetor ([exp])
                     * é necessário montar uma String que não contém isso, pois essa String
                     * será usada para recuperar informações da tabela de símbolos
                     */
                    if (identT.charAt(identT.length() - 1) == ']') {
                        int index = identT.indexOf('[');
                        identT = identT.substring(0, index);
                    }

                    //recupera símbolo na tabela usando a String construida acima
                    PropriedadesSimboloBasico psb = semantico.buscaSimbolo(identT);

                    //verifica se o símbolo está na tabela de símbolos
                    if (psb == null) {
                        semantico.erro(TipoErroSemantico.IdentificadorNaoDeclarado, linha, todaParcela.toString());
                        resultado.setTipo(TipoBasico.INTEIRO);
                    } else {
                        //atribui o tipo que foi recuperado da tabela
                        if (psb instanceof PropriedadesVariavelOuConstante) {
                            PropriedadesVariavelOuConstante pvc = (PropriedadesVariavelOuConstante) psb;
                            resultado.setTipo(pvc.getTipo());
                        } else {
                            PropriedadesVetor pv = (PropriedadesVetor) psb;
                            resultado.setTipo(pv.getTipo());
                        }
                    }

                    resultado.setValor(todaParcela.toString());
                }

                break;

            case NumeroInteiro:
                lex.append(tokenAtual.getLexema());
                obterProximo();
                resultado.setTipo(TipoBasico.INTEIRO);
                resultado.setValor(lex.toString());
                break;

            case NumeroReal:
                lex.append(tokenAtual.getLexema());
                obterProximo();
                resultado.setTipo(TipoBasico.REAL);
                resultado.setValor(lex.toString());
                break;

            case SR_AbreParenteses:
                obterProximo();
                resultado = expressao();
                reconheceToken(TipoToken.SR_FechaParenteses, true);
                resultado.setValor("(" + resultado.getValor() + ")");
                break;

            default:
                erro();
        }

        return resultado;
    }

    /**
     * parcela_nao_unario : '&' IDENTIFICADOR ('.' IDENTIFICADOR)* dimensao |
     * CADEIA_LITERAL ;
     *
     * @return Retorna objeto de variável que contém no campo valor a String que
     * representa o lexema encontrado. O tipo pode ser literal ou dependente de
     * um identificador encontrado dependendo do caminho seguido na análise no
     * momento.
     */
    public PropriedadesVariavelOuConstante parcelaNaoUnario() {
        PropriedadesVariavelOuConstante resultado = new PropriedadesVariavelOuConstante("", false);
        StringBuilder lex = new StringBuilder();
        StringBuilder todaCadeia = new StringBuilder();
        int linha = lexico.getLeitor().getLinha();
        Token ident = null;

        switch (tokenAtual.getTipo()) {
            case SR_Endereco:
                obterProximo();
                ident = reconheceToken(TipoToken.Identificador, true);
                lex.append(ident.getLexema()); //constroi o lexma sem o & para busca na tabela de símbolos 

                //('.' IDENTIFICADOR)*
                while (tokenAtual.getTipo() == TipoToken.SR_Ponto) {
                    obterProximo();
                    ident = reconheceToken(TipoToken.Identificador, true);
                    lex.append(".");
                    lex.append(ident.getLexema());
                }

                todaCadeia.append("&"); //constroi a cadeia com o lexema completo
                todaCadeia.append(lex);

                PropriedadesVetor pv = dimensao();

                //adiciona propriedades de vetor na cadeia completa
                if (pv.getDimensoes() > 0) {
                    for (String exp : pv.getTamanhoDimensoes()) {
                        todaCadeia.append("[");
                        todaCadeia.append(exp);
                        todaCadeia.append("]");
                    }
                }

                //busca na tabela de símbolos utilizando lexema construido acima
                PropriedadesSimboloBasico psb = semantico.buscaSimbolo(lex.toString());

                //verifica se símbolo foi declarado
                if (psb == null) {
                    semantico.erro(TipoErroSemantico.IdentificadorNaoDeclarado, linha, lex.toString());
                    resultado.setTipo(TipoBasico.INTEIRO);
                } else {
                    //atribui as propriedades de tipo ao resultado que será retornado no método
                    if (pv.getDimensoes() > 0) {
                        PropriedadesVetor pv2 = (PropriedadesVetor) psb;
                        resultado.setTipo(pv2.getTipo());
                    } else {
                        PropriedadesVariavelOuConstante pvc = (PropriedadesVariavelOuConstante) psb;
                        resultado.setTipo(pvc.getTipo());
                    }

                    //atribui o valor (cadeia encontrada) ao resultado
                    resultado.setValor(todaCadeia.toString());
                }

                break;

            case CadeiaLiteral:
                lex.append(tokenAtual.getLexema());
                obterProximo();
                resultado.setTipo("literal");
                resultado.setValor(lex.toString());
                break;

            default:
                erro();
        }

        return resultado;
    }

    /**
     * chamada_partes : '(' expressao (',' expressao)* ')' | ('.'
     * IDENTIFICADOR)* dimensao | ;
     *
     * @return Retorna um objeto de função que pode conter propriedades de uma
     * função (como seus parâmetros juntamente com seus tipos), mas pode retorna
     * também um objeto função que só contem propriedades de um identificador,
     * dependendo do caminho seguido dentro do método, a categoria do objeto é
     * atribuida.
     */
    public PropriedadesFuncao chamadaPartes() {
        PropriedadesFuncao resultado = new PropriedadesFuncao("");
        StringBuilder lex = new StringBuilder();
        int qParam = 0;

        switch (tokenAtual.getTipo()) {
            case SR_AbreParenteses:
                obterProximo();
                lex.append("(");
                //repupera a representação em String da expressão
                PropriedadesVariavelOuConstante pvc = expressao();
                lex.append(pvc.getValor());

                //verifica se existem parâmetros
                if (!pvc.getValor().equals("")) {
                    qParam++;
                }

                String identExp = pvc.getValor();

                /*
                 * verifica se a expressão recuperada possui propriedades de vetor 
                 * ou propriedades de função. caso encontre essa propriedades, 
                 * modifica a variável identExp para que esta representa somente
                 * o lexema para a busca na tabela de símbolos
                 */
                if (qParam > 0) {
                    if (identExp.charAt(identExp.length() - 1) == ']') {
                        int index = identExp.indexOf('[');
                        identExp = identExp.substring(0, index);
                    }

                    if (identExp.charAt(identExp.length() - 1) == ')') {
                        int index = identExp.indexOf('(');
                        identExp = identExp.substring(0, index);
                    }
                }

                //busca na tabela de símbolos utilizando a String montada acima
                PropriedadesSimboloBasico psbP = semantico.buscaSimbolo(identExp);

                List<String> tiposP = new ArrayList<String>();

                //verifica o que foi encontrado na tabela para atribuir o tipo correto
                if (psbP != null) {
                    if (psbP instanceof PropriedadesVariavelOuConstante) {
                        PropriedadesVariavelOuConstante pvc2 = (PropriedadesVariavelOuConstante) psbP;
                        tiposP.add(pvc2.getTipo());
                    } else if (psbP instanceof PropriedadesVetor) {
                        PropriedadesVetor pv2 = (PropriedadesVetor) psbP;
                        tiposP.add(pv2.getTipo());
                    } else if (psbP instanceof PropriedadesFuncao) {
                        PropriedadesFuncao pf = (PropriedadesFuncao) psbP;
                        tiposP.add(pf.getTipoRetorno());
                    }
                } else {
                    //se o identificador foi foi encontrado, então a expressão é aritmética
                    //então atribui o tipo base inteiro
                    tiposP.add(TipoBasico.INTEIRO);
                }

                // (',' expressao)*
                //repete os procedimentos anteriores caso existam outros parâmetros
                while (tokenAtual.getTipo() == TipoToken.SR_Virgula) {
                    obterProximo();
                    pvc = expressao();

                    identExp = pvc.getValor();

                    if (identExp.charAt(identExp.length() - 1) == ']') {
                        int index = identExp.indexOf('[');
                        identExp = identExp.substring(0, index);
                    }

                    if (identExp.charAt(identExp.length() - 1) == ')') {
                        int index = identExp.indexOf('(');
                        identExp = identExp.substring(0, index);
                    }


                    psbP = semantico.buscaSimbolo(identExp);
                    lex.append(", ");
                    lex.append(pvc.getValor());
                    qParam++;

                    if (psbP != null) {
                        if (psbP instanceof PropriedadesVariavelOuConstante) {
                            PropriedadesVariavelOuConstante pvc2 = (PropriedadesVariavelOuConstante) psbP;
                            tiposP.add(pvc2.getTipo());
                        } else if (psbP instanceof PropriedadesVetor) {
                            PropriedadesVetor pv2 = (PropriedadesVetor) psbP;
                            tiposP.add(pv2.getTipo());
                        } else if (psbP instanceof PropriedadesFuncao) {
                            PropriedadesFuncao pf = (PropriedadesFuncao) psbP;
                            tiposP.add(pf.getTipoRetorno());
                        }
                    } else {
                        tiposP.add(TipoBasico.INTEIRO);
                    }
                }

                reconheceToken(TipoToken.SR_FechaParenteses, true);

                lex.append(")");

                //atribui ao resultado as informações de parâmetros
                resultado.setQtdeParametros(qParam);
                resultado.setLexema(lex.toString());
                resultado.setTiposParametros(tiposP);

                break;

            case SR_Ponto:
            case SR_AbreColchetes:
                resultado.setCategoria(CategoriaSimbolo.Variavel);

                //monta o lexema para retornar ao nível superior
                // ('.' IDENTIFICADOR)*
                while (tokenAtual.getTipo() == TipoToken.SR_Ponto) {
                    obterProximo();
                    Token ident = reconheceToken(TipoToken.Identificador, true);
                    lex.append(".");
                    lex.append(ident.getLexema());
                }

                PropriedadesVetor pv = dimensao();

                //verifica propriedades de vetor e atribui lexema de acordo
                if (pv.getDimensoes() > 0) {
                    for (String exp : pv.getTamanhoDimensoes()) {
                        lex.append("[");
                        lex.append(exp);
                        lex.append("]");
                    }
                }

                //atribui o lexema ao resultado que será retornado no método
                resultado.setLexema(lex.toString());

                break;

            default:
                //atribui a categoria de variável, pois chamadaPartes resultou em epsilon
                resultado.setCategoria(CategoriaSimbolo.Variavel);
        }

        return resultado;
    }

    /**
     * exp_relacional : exp_aritmetica (('='|'&lt;>'|'>='|'&lt;='|'>'|'&lt;')
     * exp_aritmetica)* ;
     *
     * @return Retorna um objeto de variável que contém no campo valor a
     * representação em String de toda a expressão relacional. Ela é construida
     * montando pedaço por pedaço de acordo com a pilha de chamadas. O tipo
     * lógico é retornado.
     */
    public PropriedadesVariavelOuConstante expRelacional() {
        PropriedadesVariavelOuConstante exp1 = expAritmetica(); //inicia a pilha de chamadas para a expressão relacional
        Token ident = null;
        TipoToken[] tipos = {TipoToken.SR_Igual, TipoToken.SR_Diferente,
            TipoToken.SR_MaiorIgual, TipoToken.SR_MenorIgual,
            TipoToken.SR_Maior, TipoToken.SR_Menor};

        //verifica o resto da expressão relacional
        // (('='|'&lt;>'|'>='|'&lt;='|'>'|'&lt;') exp_aritmetica)*
        while (tokenAtual.getTipo() == TipoToken.SR_Igual
                || tokenAtual.getTipo() == TipoToken.SR_Diferente
                || tokenAtual.getTipo() == TipoToken.SR_MaiorIgual
                || tokenAtual.getTipo() == TipoToken.SR_MenorIgual
                || tokenAtual.getTipo() == TipoToken.SR_Maior
                || tokenAtual.getTipo() == TipoToken.SR_Menor) {

            ident = reconheceToken(tipos, true);
            PropriedadesVariavelOuConstante exp2 = expAritmetica(); //verifica demais expressões da expressão relacional

            exp1.setTipo("logico"); //atribui o tipo lógico para a expressão
            //monta o campo valor com a concatenação de expressões
            exp1.setValor(exp1.getValor() + " " + GeradorCodigo.converteOperador(ident.getLexema()) + " " + exp2.getValor());
        }

        return exp1;
    }

    /**
     * expressao : termo_logico ('ou' termo_logico)* ;
     *
     * @return Retorna um objeto de variável que contém no campo valor a
     * representação em String de toda a expressão. Ela é construida montando
     * pedaço por pedaço de acordo com a pilha de chamadas.
     */
    public PropriedadesVariavelOuConstante expressao() {
        PropriedadesVariavelOuConstante termo1 = termoLogico(); //continua a pilha de chamadas

        //verifica se existem outras expressões para serem evoluidas
        // ('ou' termo_logico)
        while (tokenAtual.getTipo() == TipoToken.PR_Ou) {
            obterProximo();
            PropriedadesVariavelOuConstante termo2 = termoLogico();
            termo1.setValor(termo1.getValor() + " || " + termo2.getValor());
        }

        return termo1;
    }

    /**
     * termo_logico : ('nao')? parcela_logica ('e' ('nao')? parcela_logica)* ;
     *
     * @return Retorna um objeto de variável que contém no campo valor a
     * representação em String de todo o termo lógico. Ela é construida montando
     * pedaço por pedaço de acordo com a pilha de chamadas.
     */
    public PropriedadesVariavelOuConstante termoLogico() {
        Token ident = reconheceToken(TipoToken.PR_Nao, false);
        PropriedadesVariavelOuConstante parcela1 = parcelaLogica(); //continua a pilha de chamadas para evoluir a expressão

        //verifica a existência do lexema 'nao' para adicionar ao valor do resultado que será retornado
        if (ident != null) {
            parcela1.setValor(" !" + parcela1.getValor());
        }

        //verifica a existência de mais parcelas lógicas
        // ('e' ('nao')? parcela_logica)*
        while (tokenAtual.getTipo() == TipoToken.PR_E) {
            obterProximo();
            ident = reconheceToken(TipoToken.PR_Nao, false);
            PropriedadesVariavelOuConstante parcela2 = parcelaLogica();

            //verifica a existência do lexema 'nao' para adicionar ao valor do resultado que será retornado
            if (ident == null) {
                parcela1.setValor(parcela1.getValor() + " && " + parcela2.getValor());
            } else {
                parcela1.setValor(parcela1.getValor() + " && !" + parcela2.getValor());
            }
        }

        return parcela1;
    }

    /**
     * parcela_logica : 'verdadeiro' | 'falso' | exp_relacional ;
     *
     * @return Retorna um objeto de variável que contém no campo valor a
     * representação em String de toda a parcela lógica. Ela é construida
     * montando pedaço por pedaço de acordo com a pilha de chamadas.
     */
    public PropriedadesVariavelOuConstante parcelaLogica() {
        PropriedadesVariavelOuConstante resultado = new PropriedadesVariavelOuConstante("", false);

        switch (tokenAtual.getTipo()) {
            case PR_Verdadeiro:
                reconheceToken(TipoToken.PR_Verdadeiro, true);
                resultado.setTipo("logico");
                resultado.setValor("1");
                break;

            case PR_Falso:
                reconheceToken(TipoToken.PR_Falso, true);
                resultado.setTipo("logico");
                resultado.setValor("0");
                break;

            default:
                //realiza uma chamada recursiva em expressão relacional para montar toda a expressão
                resultado = expRelacional();
        }

        return resultado;
    }
}
